/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.model;

import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import nl.cloudfarming.client.db.EntityManagerProvider;

/**
 *
 *
 * @author Timon Veenstra
 */
//TODO this needs some serious refactoring....
public abstract class CommonModelService {

    protected void startTransaction() {
        assert getEntityManager() != null;
        assert getEntityManager().getTransaction() != null;

        if (!getEntityManager().getTransaction().isActive()) {
            getEntityManager().getTransaction().begin();
        }
    }

    protected abstract String getModuleName();

    protected EntityManager getEntityManager() {
        return EntityManagerProvider.getDefault().getEntityManager(getModuleName());
    }


    protected void commit(){
        getEntityManager().getTransaction().commit();
    }



    public SensorMetadata findMetaDataByMetadata(String dateFormat, int sensorHeight, int numberOfSensors, String sensorSpread, String sensorType, String spectralChannels) {
        startTransaction();
        Query query = getEntityManager().createQuery(
                "select c from SensorMetadata c "
                + "where c.dateFormat = :dateFormat "
                + "and c.sensorHeight = :sensorHeight "
                + "and c.numberOfSensors = :numberOfSensors "
                + "and c.sensorSpread = :sensorSpread "
                + "and c.sensorType = :sensorType "
                + "and c.spectralChannels = :spectralChannels");
        query.setParameter("dateFormat", dateFormat);
        query.setParameter("sensorHeight", sensorHeight);
        query.setParameter("numberOfSensors", numberOfSensors);
        query.setParameter("sensorSpread", sensorSpread);
        query.setParameter("sensorType", sensorType);
        query.setParameter("spectralChannels", spectralChannels);
        try {
            return (SensorMetadata) query.getSingleResult();
        } catch (NoResultException ner) {
            return null;
        }
    }

   public SensorDataFile findFileByNameAndChecksum(String fileName, String checksum) {
        startTransaction();
        Query query = getEntityManager().createQuery(
                "select c from SensorDataFile c "
                + "where c.fileName = :fileName "
                + "and c.checksum = :checksum");
        query.setParameter("fileName", fileName);
        query.setParameter("checksum", checksum);
        try {
            return (SensorDataFile) query.getSingleResult();
        } catch (NoResultException ner) {
            return null;
        }
    }



   public List<SensorDataFile> findAllSensorDataFiles() {
        startTransaction();
        Query query = this.getEntityManager().createQuery("select c from SensorDataFile c");
        return query.getResultList();
    }

    public List<SensorMetadata> findAllSensorMetadata() {
        startTransaction();
        Query query = this.getEntityManager().createQuery("select c from SensorMetadata c");
        return query.getResultList();
    }

    public SensorMetadata findMetadataById(long id) {
        startTransaction();
        return getEntityManager().find(SensorMetadata.class, id);
    }

    public SensorDataFile findDataFileById(long id) {
        startTransaction();
        return getEntityManager().find(SensorDataFile.class, id);
    }


    public void delete(SensorMetadata toBeRemoved) {
        getEntityManager().remove(toBeRemoved);
    }

    public void delete(SensorDataFile toBeRemoved) {
        getEntityManager().remove(toBeRemoved);
    }


    protected void mergeMetadata(SensorMetadata toBeMerged) {
        SensorMetadata existing;
        //managed
        if (toBeMerged.getId() != 0) {
            getEntityManager().merge(toBeMerged);
        } // Not managed but existing in db
        else if ((existing = findMetaDataByMetadata(toBeMerged.getDateFormat(), toBeMerged.getSensorHeight(), toBeMerged.getNumberOfSensors(), toBeMerged.getSensorSpread(), toBeMerged.getSensorType(), toBeMerged.getSpectralChannels())) != null) {
            toBeMerged.setId(existing.getId());
            getEntityManager().merge(toBeMerged);
        } //New item
        else {
            getEntityManager().persist(toBeMerged);
        }
    }

    protected void mergeDataFile(SensorDataFile toBeMerged) {
        if (toBeMerged.getId() == 0) {
            getEntityManager().persist(toBeMerged);
        } else {
            toBeMerged = getEntityManager().merge(toBeMerged);
        }
    }

   public void persist(SensorDataFile<?> dataFile) {
        persist(dataFile.getMetadata());
        startTransaction();
        mergeDataFile(dataFile);
        getEntityManager().getTransaction().commit();

        for (SensorData sensorData : dataFile.getSensorData()) {
            persist(sensorData);
        }
    }

    public void persist(SensorData sensorData) {
        startTransaction();
        mergeSensorData(sensorData);
        getEntityManager().getTransaction().commit();
    }

    protected void mergeSensorData(SensorData toBeMerged) {
        if (toBeMerged.getId() == 0) {
            getEntityManager().persist(toBeMerged);
        } else {
            toBeMerged = getEntityManager().merge(toBeMerged);
        }
    }

    public void persist(SensorMetadata metadata) {
        startTransaction();
        mergeMetadata(metadata);
        getEntityManager().getTransaction().commit();
    }

    public PlotFile findPlotFileByNameChecksum(String name, String checksum) {
        startTransaction();
        Query query = getEntityManager().createQuery(
                "select p from PlotFile p "
                + "where p.fileName = :fileName "
                + "and p.checksum = :checksum");
        query.setParameter("fileName", name);
        query.setParameter("checksum", checksum);
        try {
            return (PlotFile) query.getSingleResult();
        } catch (NoResultException ner) {
            return null;
        }
    }

    public List<Plot> findAllPlots() {
        startTransaction();
        Query query = getEntityManager().createNamedQuery("findAllPLots");
        return query.getResultList();
    }

    public void persist(Object persistableObject) {
        startTransaction();
        getEntityManager().persist(persistableObject);
        getEntityManager().getTransaction().commit();
    }

    public Plot findPlotById(long id) {
        startTransaction();
        return getEntityManager().find(Plot.class, id);
    }

    public List<Culture> findCulturesByDate(Date date) {
        startTransaction();
        Query query = getEntityManager().createNamedQuery("findAllCulturesByDate");
        query.setParameter("date", date);
        return query.getResultList();
    }

}
